home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-applets-data / invest / quotes.py < prev    next >
Encoding:
Python Source  |  2009-04-23  |  7.2 KB  |  221 lines

  1. from os.path import join
  2. import gnomeapplet, gtk, gtk.gdk, gconf, gobject
  3. from gettext import gettext as _
  4. import csv
  5. from urllib import urlopen
  6. import datetime
  7. from threading import Thread
  8.  
  9. import invest, invest.about, invest.chart
  10.  
  11. CHUNK_SIZE = 512*1024 # 512 kB
  12. AUTOREFRESH_TIMEOUT = 10*60*1000 # 15 minutes
  13.  
  14. QUOTES_URL="http://finance.yahoo.com/d/quotes.csv?s=%(s)s&f=sl1d1t1c1ohgv&e=.csv"
  15.  
  16. # Sample (25/4/2008): UCG.MI,"4,86",09:37:00,2008/04/25,"0,07","4,82","4,87","4,82",11192336
  17. QUOTES_CSV_FIELDS=["ticker", ("trade", float), "time", "date", ("variation", float), ("open", float)]
  18.  
  19. # based on http://www.johnstowers.co.nz/blog/index.php/2007/03/12/threading-and-pygtk/
  20. class _IdleObject(gobject.GObject):
  21.     """
  22.     Override gobject.GObject to always emit signals in the main thread
  23.     by emmitting on an idle handler
  24.     """
  25.     def __init__(self):
  26.         gobject.GObject.__init__(self)
  27.  
  28.     def emit(self, *args):
  29.         gobject.idle_add(gobject.GObject.emit,self,*args)
  30.  
  31. class QuotesRetriever(Thread, _IdleObject):
  32.     """
  33.     Thread which uses gobject signals to return information
  34.     to the GUI.
  35.     """
  36.     __gsignals__ =  { 
  37.             "completed": (
  38.                 gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, []),
  39.             # FIXME: We don't monitor progress, yet ...
  40.             #"progress": (
  41.             #    gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [
  42.             #    gobject.TYPE_FLOAT])        #percent complete
  43.             }
  44.  
  45.     def __init__(self, tickers):
  46.         Thread.__init__(self)
  47.         _IdleObject.__init__(self)    
  48.         self.tickers = tickers
  49.         self.retrieved = False
  50.         self.data = []
  51.  
  52.     def run(self):
  53.         quotes_url = QUOTES_URL % {"s": self.tickers}
  54.         try:
  55.             quotes_file = urlopen(quotes_url, proxies = invest.PROXY)
  56.             self.data = quotes_file.readlines ()
  57.             quotes_file.close ()
  58.         except:
  59.             if invest.DEBUGGING:
  60.                 print "Error while retrieving quotes data (url = %s)" % quotes_url
  61.         else:
  62.             self.retrieved = True
  63.         self.emit("completed")
  64.  
  65.  
  66. class QuoteUpdater(gtk.ListStore):
  67.     updated = False
  68.     last_updated = None
  69.     SYMBOL, TICKER_ONLY, BALANCE, BALANCE_PCT, VALUE, VARIATION_PCT, PB = range(7)
  70.     def __init__ (self, change_icon_callback, set_tooltip_callback):
  71.         gtk.ListStore.__init__ (self, gobject.TYPE_STRING, bool, float, float, float, float, gtk.gdk.Pixbuf)
  72.         gobject.timeout_add(AUTOREFRESH_TIMEOUT, self.refresh)
  73.         self.change_icon_callback = change_icon_callback
  74.         self.set_tooltip_callback = set_tooltip_callback
  75.         self.refresh()
  76.         
  77.     def refresh(self):
  78.         if len(invest.STOCKS) == 0:
  79.             return True
  80.             
  81.         tickers = '+'.join(invest.STOCKS.keys())
  82.         quotes_retriever = QuotesRetriever(tickers)
  83.         quotes_retriever.connect("completed", self.on_retriever_completed)
  84.         quotes_retriever.start()
  85.         
  86.         self.quotes_valid = False
  87.  
  88.  
  89.     def on_retriever_completed(self, retriever):
  90.         if retriever.retrieved == False:
  91.             tooltip = [_('Invest could not connect to Yahoo! Finance')]
  92.             if self.last_updated != None:
  93.                 tooltip.append(_('Updated at %s') % self.last_updated.strftime("%H:%M"))
  94.             self.set_tooltip_callback('\n'.join(tooltip))
  95.         else:
  96.             self.populate(self.parse_yahoo_csv(csv.reader(retriever.data)))
  97.             self.updated = True
  98.             self.last_updated = datetime.datetime.now()
  99.             tooltip = []
  100.             if self.simple_quotes_count > 0:
  101.                 # Translators: This is share-market jargon. It is the percentage change in the price of a stock. The %% gets changed to a single percent sign and the %+.2f gets replaced with the value of the change.
  102.                 tooltip.append(_('Quotes average change %%: %+.2f%%') % self.avg_simple_quotes_change)
  103.             if self.positions_count > 0:
  104.                 # Translators: This is share-market jargon. It refers to the total difference between the current price and purchase price for all the shares put together. i.e. How much money would be earned if they were sold right now.
  105.                 tooltip.append(_('Positions balance: %+.2f') % self.positions_balance)
  106.             tooltip.append(_('Updated at %s') % self.last_updated.strftime("%H:%M"))
  107.             self.set_tooltip_callback('\n'.join(tooltip))
  108.     
  109.  
  110.  
  111.     def parse_yahoo_csv(self, csvreader):
  112.         result = {}
  113.         for fields in csvreader:
  114.             if len(fields) == 0:
  115.                 continue
  116.  
  117.             result[fields[0]] = {}
  118.             for i, field in enumerate(QUOTES_CSV_FIELDS):
  119.                 if type(field) == tuple:
  120.                     try:
  121.                         result[fields[0]][field[0]] = field[1](fields[i])
  122.                     except:
  123.                         result[fields[0]][field[0]] = 0
  124.                 else:
  125.                     result[fields[0]][field] = fields[i]
  126.             # calculated fields
  127.             try:
  128.                 result[fields[0]]['variation_pct'] = result[fields[0]]['variation'] / float(result[fields[0]]['trade'] - result[fields[0]]['variation']) * 100
  129.             except ZeroDivisionError:
  130.                 result[fields[0]]['variation_pct'] = 0
  131.         return result 
  132.  
  133.     def populate(self, quotes):
  134.         self.clear()
  135.         
  136.         if (len(quotes) == 0):
  137.             self.quotes_valid = False
  138.             return
  139.         else:
  140.             self.quotes_valid = True
  141.  
  142.         try:
  143.             quote_items = quotes.items ()
  144.             quote_items.sort ()
  145.     
  146.             simple_quotes_change = 0
  147.             self.simple_quotes_count = 0
  148.             self.positions_balance = 0
  149.             self.positions_count = 0
  150.     
  151.             for ticker, val in quote_items:
  152.                 pb = None
  153.                 
  154.                 # Check whether the symbol is a simple quote, or a portfolio value
  155.                 is_simple_quote = True
  156.                 for purchase in invest.STOCKS[ticker]:
  157.                     if purchase["amount"] != 0:
  158.                         is_simple_quote = False
  159.                         break
  160.                 
  161.                 if is_simple_quote:
  162.                     self.simple_quotes_count += 1
  163.                     row = self.insert(0, [ticker, True, 0, 0, val["trade"], val["variation_pct"], pb])
  164.                     simple_quotes_change += val['variation_pct']
  165.                 else:
  166.                     self.positions_count += 1
  167.                     current = sum([purchase["amount"]*val["trade"] for purchase in invest.STOCKS[ticker] if purchase["amount"] != 0])
  168.                     paid = sum([purchase["amount"]*purchase["bought"] + purchase["comission"] for purchase in invest.STOCKS[ticker] if purchase["amount"] != 0])
  169.                     balance = current - paid
  170.                     if paid != 0:
  171.                         change = 100*balance/paid
  172.                     else:
  173.                         change = 100 # Not technically correct, but it will look more intuitive than the real result of infinity.
  174.                     row = self.insert(0, [ticker, False, balance, change, val["trade"], val["variation_pct"], pb])
  175.                     self.positions_balance += balance
  176.     
  177.                 if len(ticker.split('.')) == 2:
  178.                     url = 'http://ichart.europe.yahoo.com/h?s=%s' % ticker
  179.                 else:
  180.                     url = 'http://ichart.yahoo.com/h?s=%s' % ticker
  181.                 
  182.                 image_retriever = invest.chart.ImageRetriever(url)
  183.                 image_retriever.connect("completed", self.set_pb_callback, row)
  184.                 image_retriever.start()
  185.                 
  186.             if self.simple_quotes_count > 0:
  187.                 self.avg_simple_quotes_change = simple_quotes_change/float(self.simple_quotes_count)
  188.             else:
  189.                 self.avg_simple_quotes_change = 0
  190.     
  191.             if self.avg_simple_quotes_change != 0:
  192.                 simple_quotes_change_sign = self.avg_simple_quotes_change / abs(self.avg_simple_quotes_change)
  193.             else:
  194.                 simple_quotes_change_sign = 0
  195.     
  196.             # change icon
  197.             if self.simple_quotes_count > 0:
  198.                 self.change_icon_callback(simple_quotes_change_sign)
  199.             else:
  200.                 positions_balance_sign = self.positions_balance/abs(self.positions_balance)
  201.                 self.change_icon_callback(positions_balance_sign)
  202.         except:
  203.             self.quotes_valid = False
  204.  
  205.     def set_pb_callback(self, retriever, row):
  206.         self.set_value(row, 6, retriever.image.get_pixbuf())
  207.     
  208.     # check if we have only simple quotes
  209.     def simple_quotes_only(self):
  210.         res = True
  211.         for entry, value in invest.STOCKS.iteritems():
  212.             for purchase in value:
  213.                 if purchase["amount"] != 0:
  214.                     res = False
  215.                     break
  216.         return res
  217.  
  218. if gtk.pygtk_version < (2,8,0):
  219.     gobject.type_register(QuoteUpdater)
  220.  
  221.